# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# @Create Time  : 2024/4/2 17:43
# @Author       : kony.xie
# @File Name    : job.py
# @Python Ver.  : Python 3.8.10
# @Description  :
# -------------------------------------------------------------------------------
from apscheduler.jobstores.base import JobLookupError
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.triggers.cron import CronTrigger
from croniter import croniter
import datetime
import logging
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.pool import QueuePool

# 创建数据库引擎，使用连接池
engine = create_engine(
    'mysql+pymysql://root:123456@10.12.15.104/atp4p?charset=utf8',
    poolclass=QueuePool,
    pool_size=10,
    max_overflow=20,
    pool_recycle=3600  # 定期回收连接，单位是秒
)

# 创建会话
db_session = scoped_session(sessionmaker(bind=engine))

logging.basicConfig(
    level=logging.DEBUG,  # 设置日志级别为INFO，这样ERROR级别及以上的日志都会被记录
    filename='./log/django.log',
    filemode='a',  # 以追加模式打开日志文件
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'  # 设置日志格式
)
logger = logging.getLogger(__name__)

class Job:
    def __init__(self):
        # Set the database information, will change to read from the configuration file
        # Table name
        table_name = 'base_job'
        job_stores = {
            # The name of the scheduled task table is base_job
            'default': SQLAlchemyJobStore(engine=engine, tablename=table_name)
        }
        self.scheduler = BackgroundScheduler(jobstores=job_stores, timezone='Asia/Shanghai')
        # Since the instantiation of the job class is implemented in ready(), every time django is started, the scheduler starts and automatically reads the job in the database
        self.scheduler.start()
        logger.info( "Scheduler started with jobs: %s", self.scheduler.get_jobs() )

    # The method used to test
    @staticmethod
    def test():
        print(f"Current time:{datetime.datetime.now()}")

    # The execution function of job, and calls the run_case method to start the test
    @staticmethod
    def execute(project):
        from lib.execute import Execute
        execute = Execute( project )
        run_result = execute.run_case( model="project" )
        logger.info(run_result)

    def create_job(self, project, cron):
        if len(cron) == 0:
            return True
        else:
            try:
                croniter( cron )
                data_list = cron.split()
                self.scheduler.add_job( Job.execute, 'cron', id=project, minute=data_list[0], hour=data_list[1], day=data_list[2], month=data_list[3], day_of_week=data_list[4], timezone='Asia/Shanghai', replace_existing=True, args=[project] )
                return True
            except ValueError:
                return False

    def update_job(self, project, cron):
        if len(cron)==0:
            try:
                self.delete_job(project)
                logger.info( "Job deleted: %s", project )
                return True
            except JobLookupError as e:
                # 原来就没有定时任务的情况
                logger.error( "Job not found: %s", project )
                return True
            except Exception as e:
                logger.error( "Error deleting job: %s, Error: %s", project, str( e ) )
                return False
        else:
            try:
                croniter( cron )
                data_list = cron.split()
                try:
                    job = self.scheduler.reschedule_job( project, trigger=CronTrigger(minute=data_list[0], hour=data_list[1], day=data_list[2], month=data_list[3], day_of_week=data_list[4] ) )
                except JobLookupError:
                    logger.warning( "Job not found, will create new: %s", project )
                    # modify_job cannot modify the time parameter, we need create new job to update
                    self.scheduler.pause_job(project)
                    self.scheduler.remove_job(project)
                    # If the update does not exist, ignore the exception and create a update job
                    job = self.scheduler.add_job( Job.execute, 'cron', id=project, minute=data_list[0], hour=data_list[1], day=data_list[2], month=data_list[3], day_of_week=data_list[4], timezone='Asia/Shanghai', replace_existing=True, args=[project] )
                except Exception as e:
                    logger.info(f"Update trigger failed: {e}")
                logger.info( "Job updated: %s", project )
                logger.info(f"next run time is {job.next_run_time}")
                return True
            except ValueError as e:
                logger.error( "Invalid cron expression: %s, Error: %s", cron, str( e ) )
                return False
            except Exception as e:
                logger.error( "Error updating job: %s, Error: %s", project, str( e ) )
                return False

    def delete_job(self, project):
        self.scheduler.pause_job(project)
        self.scheduler.remove_job(project)
